package org.xx.armory.commons;

import org.apache.commons.lang3.RegExUtils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.function.Supplier;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static java.lang.System.arraycopy;
import static java.util.Base64.getEncoder;
import static java.util.regex.Pattern.compile;
import static org.apache.commons.lang3.StringUtils.startsWithIgnoreCase;
import static org.apache.commons.lang3.StringUtils.substring;
import static org.apache.commons.lang3.StringUtils.trimToEmpty;
import static org.xx.armory.commons.Validators.greaterThanOrEqual;
import static org.xx.armory.commons.Validators.notBlank;
import static org.xx.armory.commons.Validators.notNull;

/**
 * 常用功能
 *
 * @author Haart
 */
public final class SysUtils {
    private static final Pattern SPLIT_PATTERN = compile(",", Pattern.LITERAL);
    private static final Pattern HASH_NORM_FILTER = compile("[/*%=+-]");
    private static final String CLASS_PATH_PREFIX = "classpath://";
    private final static Pattern ENV_VARIABLE_NAME = compile("\\$\\{([a-z0-9_.-]+)}", Pattern.DOTALL | Pattern.CASE_INSENSITIVE);
    private static final Pattern STRING_FORMAT_PLACEHOLDER = compile("(?<!\\{)\\{}",
                                                                     Pattern.DOTALL | Pattern.CASE_INSENSITIVE);

    private SysUtils() {
        throw new UnsupportedOperationException();
    }

    /**
     * 连接字符串
     *
     * <p>
     * 将任意个字符串参数按照传入的顺序首尾相连
     * </p>
     * <p>
     * 如果传入的参数为{@code null}或者长度为{@code 0}, 那么返回值为空字符串。
     * </p>
     * <p>
     * <strong>如果某个字符串参数是{@code null}, 那么该字符串会被跳过。</strong>
     * </p>
     *
     * <pre>
     * 示例：
     *
     * concat() == "";
     * concat("j", "um", "p") == "jump";
     * concat("a", null, "d") == "ad";
     * </pre>
     *
     * @param first
     *         用于连接的第一个字符串
     * @param others
     *         用于连接的其它字符串。
     * @return 将字符串参数按照传入的顺序首尾连接得到的字符串。
     * @see java.lang.StringBuilder#append(String)
     */
    public static String concat(
            String first,
            String... others
    ) {
        if (others == null) {
            return first != null ? first : "";
        }

        int l = first != null ? first.length() : 0;
        for (String s : others) {
            l += s.length();
        }

        final StringBuilder sb = new StringBuilder(l);
        if (first != null) {
            sb.append(first);
        }
        for (final String s : others) {
            if (s != null) {
                sb.append(s);
            }
        }

        return sb.toString();
    }

    /**
     * 使用逗号分割字符串，去掉空白部分。
     *
     * <p>
     * 示例：
     * </p>
     *
     * <pre>
     * split(null) = []
     * split("") = []
     * split("  ") = []
     * split("1") = ["1"]
     * split("1,abc,6") = ["1", "abc", "6"]
     * split("1,abc,6,") = ["1", "abc", "6"]
     * split(",abc  def, km ,  ,6,") = ["abc  def", "km", "6"]
     * </pre>
     *
     * @param s
     *         被分割的字符串。
     * @return 分割的结果。
     */
    public static Collection<String> split(
            String s
    ) {
        if (s == null) {
            return Collections.emptyList();
        }

        final String[] ss = SPLIT_PATTERN.split(s);
        final ArrayList<String> ret = new ArrayList<>(ss.length);
        for (final String term : ss) {
            final String term1 = term.trim();
            if (!term1.isEmpty()) {
                ret.add(term1);
            }
        }
        return ret;
    }

    /**
     * 关闭一个<code>java.lang.AutoCloseable</code>类型的对象。
     *
     * <p>
     * 如果<code>closable</code>参数为 {@code null} ，则不执行任何关闭操作。
     * </p>
     * <p>
     * 否则调用<code>java.lang.AutoCloseable.close()</code>方法关闭此对象，同时拦截所有抛出的异常。
     * </p>
     *
     * @param closable
     *         需要关闭的<code>java.lang.AutoCloseable</code>类型的对象
     * @see java.lang.AutoCloseable#close()
     */
    public static void closeQuietly(AutoCloseable closable) {
        if (closable == null) {
            return;
        }

        try {
            closable.close();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    /**
     * 关闭一个<code>java.sql.Connection</code>类型的对象。
     *
     * <p>
     * 如果<code>connection</code>参数为 {@code null} ，则不执行任何关闭操作。
     * </p>
     * <p>
     * 否则调用<code>java.sql.Connection.close()</code>方法关闭此对象，同时拦截所有抛出的
     * <code>java.sql.SQLException</code>异常。
     * </p>
     *
     * @param connection
     *         需要关闭的<code>java.sql.Connection</code>类型的对象
     * @see java.sql.Connection#close()
     * @see java.sql.SQLException
     */
    public static void closeQuietly(
            java.sql.Connection connection
    ) {
        if (connection == null) {
            return;
        }

        try {
            connection.close();
        } catch (java.sql.SQLException ex) {
            ex.printStackTrace();
        }
    }

    /**
     * 关闭一个<code>java.sql.Statement</code>类型的对象。
     *
     * <p>
     * 如果<code>statement</code>参数为 {@code null} ，则不执行任何关闭操作。
     * </p>
     * <p>
     * 否则调用<code>java.sql.Statement.close()</code>方法关闭此对象，同时拦截所有抛出的
     * <code>java.sql.SQLException</code>异常。
     * </p>
     *
     * @param statement
     *         需要关闭的<code>java.sql.Statement</code>类型的对象
     * @see java.sql.Statement#close()
     * @see java.sql.SQLException
     */
    public static void closeQuietly(
            java.sql.Statement statement
    ) {
        if (statement == null) {
            return;
        }

        try {
            statement.close();
        } catch (java.sql.SQLException ex) {
            ex.printStackTrace();
        }
    }

    /**
     * 关闭一个<code>java.sql.ResultSet</code>类型的对象。
     *
     * <p>
     * 如果<code>rs</code>参数为 {@code null} ，则不执行任何关闭操作。
     * </p>
     * <p>
     * 否则调用<code>java.sql.ResultSet.close()</code>方法关闭此对象，同时拦截所有抛出的
     * <code>java.sql.SQLException</code>异常。
     * </p>
     *
     * @param rs
     *         需要关闭的<code>java.sql.ResultSet</code>类型的对象
     * @see java.sql.ResultSet#close()
     * @see java.sql.SQLException
     */
    public static void closeQuietly(
            java.sql.ResultSet rs
    ) {
        if (rs == null) {
            return;
        }

        try {
            rs.close();
        } catch (java.sql.SQLException ex) {
            ex.printStackTrace();
        }
    }

    /**
     * 从输入流中读取指定长度的内容，并且以字节数组的形式返回。
     *
     * @param stream
     *         用于读取数据的输入流。
     * @param maxSize
     *         指定读取数据的最大长度。如果此参数的值小于等于<code>0</code>则不限制长度。
     * @return 输入流中读取的数据，如果输入流是 {@code null} 或者输入流中没有数据可以读取，那么返回长度为0的字节数组。
     * @throws java.io.IOException
     *         如果参数 {@code stream} 无法读取，或者已被关闭，或者出现其它IO错误。
     * @see java.io.InputStream#read(byte[], int, int)
     */
    public static byte[] read(
            java.io.InputStream stream,
            long maxSize
    )
            throws java.io.IOException {
        if (stream == null) {
            return new byte[0];
        }

        final java.io.ByteArrayOutputStream outputStream = new java.io.ByteArrayOutputStream();

        final int BUFFER_SIZE = 1024;
        final byte[] buffer = new byte[BUFFER_SIZE];

        try {
            int rl = stream.read(buffer);
            while (rl != -1) {
                outputStream.write(buffer, 0, rl);
                rl = stream.read(buffer);
            }
        } finally {
            try {
                outputStream.close();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }

        return outputStream.toByteArray();
    }

    /**
     * 从输入流中读取全部内容，并且以字节数组的形式返回。
     *
     * @param stream
     *         用于读取数据的输入流。
     * @return 输入流中读取的全部数据，如果输入流是 {@code null} 或者输入流中没有数据可以读取，那么返回长度为0的字节数组。
     * @throws java.io.IOException
     *         如果参数 {@code stream} 无法读取，或者已被关闭，或者出现其它IO错误。
     * @see java.io.InputStream#read(byte[], int, int)
     */
    public static byte[] read(
            java.io.InputStream stream
    )
            throws java.io.IOException {
        return read(stream, -1);
    }

    /**
     * 从输入流中读取全部内容，并且以字符串的形式返回，按照UTF-8编码格式进行解码。
     *
     * @param stream
     *         用于读取数据的输入流。
     * @return 输入流中读取的全部数据，如果输入流是 {@code null} 或者输入流中没有数据可以读取，那么返回空字符串。
     * @throws java.io.IOException
     *         如果参数 {@code stream} 无法读取，或者已被关闭，或者出现其它IO错误。
     * @see #readString(java.io.InputStream, String)
     */
    public static String readString(
            java.io.InputStream stream
    )
            throws IOException {
        return readString(stream, "UTF-8");
    }

    /**
     * 从输入流中读取全部内容，并且以字符串的形式返回，按照指定的格式进行解码。
     *
     * @param stream
     *         用于读取数据的输入流。
     * @param charset
     *         用于将字节解码为字符的编码。
     * @return 输入流中读取的全部数据，如果输入流是 {@code null} 或者输入流中没有数据可以读取，那么返回空字符串。
     * @throws java.io.IOException
     *         如果参数 {@code stream} 无法读取，或者已被关闭。
     * @see java.io.Reader#read(char[], int, int)
     */
    public static String readString(
            java.io.InputStream stream,
            Charset charset
    )
            throws java.io.IOException {
        if (stream == null) {
            return "";
        }

        final int BUFFER_SIZE = 1024;
        final char[] buffer = new char[BUFFER_SIZE];
        final StringBuilder sb = new StringBuilder();

        java.io.Reader r = new java.io.BufferedReader(new java.io.InputStreamReader(stream, charset));
        int rl = r.read(buffer);
        while (rl != -1) {
            sb.append(buffer, 0, rl);
            rl = r.read(buffer);
        }

        return sb.toString();
    }

    /**
     * 从输入流中读取全部内容，并且以字符串的形式返回，按照指定的格式进行解码。
     *
     * @param stream
     *         用于读取数据的输入流。
     * @param charsetName
     *         用于将字节编码为字符的字符集名称
     * @return 输入流中读取的全部数据，如果输入流是 {@code null} 或者输入流中没有数据可以读取，那么返回空字符串。
     * @throws IllegalArgumentException
     *         如果参数 {@code charsetName} 是{@code null}或者只包含空白字符。
     * @throws java.io.IOException
     *         如果参数 {@code stream} 无法读取，或者已被关闭，或者指定的字符集名称无效。
     * @see #readString(InputStream, Charset)
     */
    public static String readString(
            java.io.InputStream stream,
            String charsetName
    )
            throws java.io.IOException {
        charsetName = notBlank(charsetName, "charsetName").trim();
        return readString(stream, Charset.forName(charsetName));
    }

    /**
     * 从{@code java.io.Reader}中读取全部文本，并以字符串的形式返回。
     *
     * @param r
     *         用于读取文本的{@code java.io.Reader}对象。
     * @return 从{@code java.io.Reader}对象读取的全部数据，如果输入流是 {@code null} 或者输入流中没有数据可以读取，那么返回空字符串。
     * @throws java.io.IOException
     *         如果参数<code>r</code>无法读取，或者已被关闭，或者出现其它IO错误。
     * @see java.io.Reader#read(char[], int, int)
     */
    public static String readString(
            java.io.Reader r
    )
            throws IOException {
        if (r == null) {
            return "";
        }

        final int BUFFER_SIZE = 1024;
        final char[] buffer = new char[BUFFER_SIZE];
        final StringBuilder sb = new StringBuilder();

        int rl = r.read(buffer);
        while (rl != -1) {
            sb.append(buffer, 0, rl);
            rl = r.read(buffer);
        }

        return sb.toString();
    }

    /**
     * 合并两个容器中的数据。
     *
     * <p>
     * 如果容器{@code c1}为{@code null}, 则只返回容器{@code c2}的元素。如果容器{@code c2}为{@code null}, 则只返回容器
     * {@code c1} 的元素。如果两个容器都是{@code null}, 则返回的结果不包含任何元素。
     * </p>
     *
     * @param <V>
     *         容器的元素类型。
     * @param c1
     *         需要被合并的容器。
     * @param c2
     *         需要被合并的容器。
     * @return 包含了容器{@code c1}和{@code c2}中所有的数据的集合。
     * <ul>
     * <li>相同的元素在返回的集合中只会包含一次。</li>
     * <li>不保证元素被添加的顺序和原容器相同。</li>
     * </ul>
     * @see java.util.Set#addAll(Collection)
     */
    public static <V> Set<? super V> merge(
            Collection<? extends V> c1,
            Collection<? extends V> c2
    ) {
        final Set<V> ret = new LinkedHashSet<>();

        if (c1 != null) {
            ret.addAll(c1);
        }
        if (c2 != null) {
            ret.addAll(c2);
        }

        return ret;
    }

    /**
     * 合并多个JavaBean，并将字段和值写入到<code>java.util.Map</code>中。
     *
     * @param entities
     *         用于合并的JavaBeans，忽略其中值为 {@code null} 的对象。
     * @return 包含了所有JavaBean的字段和值的<code>java.util.Map</code>。
     */
    public static Map<String, Object> merge(
            Object... entities
    ) {
        final Map<String, Object> ret = new HashMap<>();

        if (entities != null) {
            for (final Object entity : entities) {
                if (entity == null) {
                    continue;
                }

                final Class<?> entityClass = entity.getClass();
                final Method[] methods = entityClass.getMethods();
                for (final Method m : methods) {
                    if ((m.getModifiers() & Modifier.STATIC) != 0) {
                        continue;
                    }
                    if (m.getParameterTypes().length != 0) {
                        continue;
                    }
                    final String mName = m.getName();
                    if ("get".equals(mName) || "is".equals(mName) || "getClass".equals(mName)) {
                        continue;
                    }

                    String pName;
                    if (mName.startsWith("get")) {
                        if (mName.length() > 4) {
                            pName = mName.substring(3, 4).toLowerCase() + mName.substring(4);
                        } else {
                            pName = mName.substring(3, 4).toLowerCase();
                        }
                    } else if (mName.startsWith("is")) {
                        if (mName.length() > 3) {
                            pName = mName.substring(2, 3).toLowerCase() + mName.substring(3);
                        } else {
                            pName = mName.substring(2, 3).toLowerCase();
                        }
                    } else {
                        continue;
                    }

                    m.setAccessible(true);
                    try {
                        ret.put(pName, m.invoke(entity));
                    } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
                        ex.printStackTrace();
                    }
                }
            }
        }

        return Collections.unmodifiableMap(ret);
    }

    /**
     * 为指定的文件打开输入流。
     *
     * @param fileName
     *         文件名。
     * @return 为指定的文件打开的输入流。
     * @throws IllegalArgumentException
     *         如果参数{@code fileName}是{@code null}或者只包含空白字符。
     * @throws FileNotFoundException
     *         如果指定的文件不存在或者无法读取。
     * @throws SecurityException
     *         如果没有足够的权限读取指定的文件。
     */
    public static InputStream openInputStream(
            String fileName
    )
            throws FileNotFoundException, SecurityException {
        fileName = notBlank(fileName, "fileName").trim();

        if (startsWithIgnoreCase(fileName, CLASS_PATH_PREFIX)) {
            final String resourceName = fileName.substring(CLASS_PATH_PREFIX.length());
            final InputStream stream = Thread.currentThread().getContextClassLoader().getResourceAsStream(resourceName);
            if (stream == null) {
                throw new FileNotFoundException("cannot find resource " + fileName);
            }
            return stream;
        } else {
            return new FileInputStream(fileName);
        }
    }

    /**
     * 为指定的文件创建URL。
     *
     * @param fileName
     *         文件名。
     * @return 对应的URL。
     * @throws IllegalArgumentException
     *         如果参数{@code fileName}是{@code null}或者只包含空白字符。
     * @throws FileNotFoundException
     *         如果指定的文件不存在。
     * @throws MalformedURLException
     *         指定的文件无法被表示为URL形式。
     */
    public static URL getFileURL(
            String fileName
    )
            throws FileNotFoundException, MalformedURLException {
        fileName = notBlank(fileName, "fileName").trim();

        if (startsWithIgnoreCase(fileName, CLASS_PATH_PREFIX)) {
            final String resourceName = fileName.substring(CLASS_PATH_PREFIX.length());
            final URL url = Thread.currentThread().getContextClassLoader().getResource(resourceName);
            if (url == null) {
                throw new FileNotFoundException("cannot find resource " + fileName);
            }
            return url;
        } else {
            return new File(fileName).toURI().toURL();
        }
    }

    /**
     * 创建一系列目录，包括指定的目录和它的所有上级目录。
     *
     * @param dir
     *         待创建的目录。
     * @return 如果指定目录创建成功或者已存在则返回 {@code true}，否则返回{@code false}。
     * @throws IllegalArgumentException
     *         如果参数{@code dir}是{@code null}。
     */
    public static boolean mkdirs(
            File dir
    ) {
        notNull(dir, "dir");

        return dir.mkdirs() || dir.exists() && dir.isDirectory();
    }

    /**
     * 按照给定的数值，计算对应的散列字节数组。
     *
     * <p>
     * 为了保证散列字节数组的有效性，用于计算的原始值应当尽量保证唯一性。
     * </p>
     *
     * @param srcObjects
     *         用于计算散列字节数组的原始值，可以是任意类型的值，包括 {@code null} 。
     * @return 根据原始值计算出来的散列字节数组，该数组的长度不小于20。
     * @throws java.lang.NullPointerException
     *         如果参数{@code srcObjects}的值是{@code null}。
     * @see CryptographicUtils#hash(int)
     */
    public static byte[] uniqueHashBytes(
            Object[] srcObjects
    ) {
        notNull(srcObjects, "srcObjects");

        int r = 17;
        for (final Object src : srcObjects) {
            r = r * 31 + (src == null ? 0 : src.hashCode());
        }

        return CryptographicUtils.hash(r);
    }

    /**
     * 按照给定的数值，计算对应的散列字符串，该字符串的长度固定为6。
     *
     * <p>
     * 为了保证散列字节串的有效性，用于计算的原始值应当尽量保证唯一性。
     * </p>
     * <p>
     * 散列字符串只包含数字、小写英文字母、大写英文字母和连字符(-)。
     * </p>
     *
     * @param srcObjects
     *         用于计算散列字符串的原始值，可以是任意类型的值，包括 {@code null} 。
     * @return 根据原始值计算出来的字符串，该字符串的长度固定为6。
     * @throws java.lang.NullPointerException
     *         如果参数<code>srcObjects</code>的值是 {@code null} 。
     * @see #uniqueHashBytes(Object[])
     */
    public static String uniqueHash(
            Object[] srcObjects
    ) {
        return HASH_NORM_FILTER
                .matcher(getEncoder().encodeToString(uniqueHashBytes(srcObjects)).substring(2, 8))
                .replaceAll("-");
    }

    /**
     * 复制一个字符串数组。
     *
     * <p>
     * 复制后的新字符串数组和原字符串数组的长度、内容、顺序完全一样，但是引用了不同的数组对象。
     * </p>
     *
     * @param src
     *         原字符串数组。
     * @return 复制后的新字符串数组。如果原字符串数组是{@code null}，那么复制后的新字符串长度为0。
     */
    public static String[] clone(
            String[] src
    ) {
        if (src == null) {
            return new String[0];
        } else {
            final String[] ret = new String[src.length];
            arraycopy(src, 0, ret, 0, ret.length);
            return ret;
        }
    }

    /**
     * 判断指定的数组是否是{@code null}或者是空数组。
     *
     * @param array
     *         判断的数组。
     * @return 如果该数组是 {@code null}或者是空数组(长度为0)则返回{@code true}，否则返回{@code false}。
     */
    public static boolean notEmpty(
            Object array
    ) {
        return array == null || Array.getLength(array) != 0;
    }

    /**
     * 判断两个字符串是否相等。
     *
     * @param s1
     *         待比较的字符串。
     * @param s2
     *         另一个待比较的字符串。
     * @return 如果相等则返回{@code true}，否则返回{@code false}，如果两个参数都是{@code null}也返回{@code true}。
     */
    public static boolean stringEquals(
            String s1,
            String s2
    ) {
        return Objects.equals(s1, s2);
    }

    /**
     * 判断两个字符串是否相等，比较时忽略大小写。
     *
     * @param s1
     *         待比较的字符串。
     * @param s2
     *         另一个待比较的字符串。
     * @return 如果相等则返回{@code true}，否则返回{@code false}，如果两个参数都是{@code null}也返回{@code true}。
     */
    public static boolean stringEqualsIgnoreCase(
            String s1,
            String s2
    ) {
        return s1 != null ? s1.equalsIgnoreCase(s2) : s2 == null;
    }

    /**
     * 从字符串中删除所有的非数字字符。
     *
     * <p>
     * 此方法通常用于过滤所有的非数字字符，使得字符串能够通过{@code Integer.valueOf()}之类的方法。
     * </p>
     *
     * @param s
     *         原字符串
     * @return 删除了所有非数字字符之后的字符串，如果{@code s == null}则返回空字符串。
     * @see RegExUtils#removePattern(String, String)
     */
    public static String removeNonDigit(
            String s
    ) {
        return s == null ? "" : RegExUtils.removePattern(s, "[^0-9+.-]");
    }

    /**
     * 从字符串中删除所有的非十六进制数字字符。
     *
     * <p>
     * 此方法通常用于过滤所有的非十六进制数字字符，使得字符串能够通过{@code Integer.valueOf()}之类的方法。
     * </p>
     *
     * @param s
     *         原字符串
     * @return 删除了所有非数字字符之后的字符串，如果{@code s == null}则返回空字符串。
     * @see RegExUtils#removePattern(String, String)
     */
    public static String removeNonHex(
            String s
    ) {
        return s == null ? "" : RegExUtils.removePattern(s, "[^0-9a-zA-Z]");
    }

    /**
     * 判断给定的{@code java.math.BigDecimal}对象是否是0。
     *
     * @param d
     *         需判断的{@code java.math.BigDecimal}对象
     * @return 如果 d 的值是0，那么返回{@code true}，否则返回{@code false}。
     * @throws java.lang.NullPointerException
     *         如果参数 {@code d == null}。
     */
    public static boolean isZero(
            java.math.BigDecimal d
    ) {
        notNull(d, "d");

        return d.equals(java.math.BigDecimal.ZERO);
    }

    /**
     * 使用正则表达式进行替换。
     * <p>
     * 参数{@code producer}返回的替换字符串中可以包含捕获引用。所以反斜线{@literal \}和美元符{@literal $}具有特殊含义，如果需要把这2个字符看作字面量则需要使用反斜线转义。
     * </p>
     *
     * @param pattern
     *         正则表达式。
     * @param src
     *         被替换的原始字符串，如果此参数是{@code null}则返回空字符串。。
     * @param producer
     *         生成替换结果的生成器，每次匹配的时候都会调用此生成器。
     * @return 替换结果。
     * @throws IllegalArgumentException
     *         如果参数{@code pattern}或者{@code producer}是{@code null}。
     * @see Matcher#appendReplacement(StringBuffer, String)
     * @see Matcher#appendTail(StringBuffer)
     */
    public static String replaceByRegex(
            Pattern pattern,
            String src,
            ReplacementProducer producer
    ) {
        notNull(pattern, "pattern");
        notNull(producer, "producer");

        if (src == null) {
            return "";
        }

        final StringBuffer buffer = new StringBuffer();
        final Matcher matcher = pattern.matcher(src);
        int i = 0;
        String lastMatched = "";
        while (matcher.find()) {
            matcher.appendReplacement(buffer, producer.execute(i++, matcher, lastMatched));
            lastMatched = matcher.group();
        }
        matcher.appendTail(buffer);

        return buffer.toString();
    }

    /**
     * 将指定的列表的长度调整到期望的大小。
     *
     * @param list
     *         需调整长度的列表。
     * @param newSize
     *         新的长度。
     * @throws IllegalArgumentException
     *         如果参数{@code list}是{@code null}，或者采纳数{@code newSize}小于{@code 0}。
     */
    public static void resize(
            List<?> list,
            int newSize
    ) {
        notNull(list, "list");
        greaterThanOrEqual(newSize, "newSize", 0);

        if (newSize == 0) {
            list.clear();
        }

        // 如果列表的长度不是期望的长度，那么需要调整列表。
        if (newSize != list.size()) {
            if (newSize > list.size()) {
                // 需要扩充到指定的长度。
                int i = list.size();
                while (i < newSize) {
                    list.add(null);
                    ++i;
                }
            } else {
                // 需要缩减到指定的长度。
                while (list.size() > newSize) {
                    list.remove(list.size() - 1);
                }
            }
        }
    }

    /**
     * 从源集合中复制元素到目标列表，如果源集合的元素数量不足或者是{@code null}，则调用{@code defaultSupplier}生成默认元素。
     *
     * @param src
     *         源集合。如果此参数是{@code null}，那么所有的默认元素都通过生成器生成。
     * @param defaultSupplier
     *         生成默认元素的生成器。如果此参数是{@code null}则默认元素也是{@code null}。
     * @param dst
     *         目标列表。
     * @param <E>
     *         集合元素的类型。
     * @throws IllegalArgumentException
     *         如果参数{@code dst}是{@code null}。
     */
    public static <E> void copyWithDefault(
            Collection<E> src,
            Supplier<E> defaultSupplier,
            List<E> dst
    ) {
        notNull(dst, "dst");

        int i = 0;
        if (src != null) {
            for (final E element : src) {
                if (i >= dst.size()) {
                    break;
                }

                dst.set(i, element);
                ++i;
            }
        }

        while (i < dst.size()) {
            final E element = defaultSupplier != null ? defaultSupplier.get() : null;
            dst.set(i, element);
            ++i;
        }
    }

    /**
     * 从源数组中复制元素到新数组，如果源数组的元素数量不足或者是{@code null}，则调用{@code defaultSupplier}生成默认元素。
     *
     * @param origin
     *         源数组。
     * @param defaultSupplier
     *         生成默认元素的生成器。如果此参数是{@code null}则默认元素也是{@code null}。
     * @param newLength
     *         新数组的长度，返回的结果的长度必须等于{@code size}。
     * @param <T>
     *         数组元素的类型。
     * @return 复制结果数组。
     * @throws IllegalArgumentException
     *         如果参数{@code origin}是{@code null}，或者参数{@code size}小于{@code 0}。
     */
    public static <T> T[] copyArrayWithDefault(
            T[] origin,
            Supplier<T> defaultSupplier,
            int newLength
    ) {
        notNull(origin, "origin");
        greaterThanOrEqual(newLength, "newLength", 0);

        final T[] result = Arrays.copyOf(origin, newLength);

        for (int i = origin.length; i < result.length; ++i) {
            result[i] = defaultSupplier != null ? defaultSupplier.get() : null;
        }

        return result;
    }

    /**
     * 获取指定数组的指定下标的元素，如果超过数组的上限则返回默认值。
     *
     * @param array
     *         数组。
     * @param index
     *         下标。
     * @param defaultValue
     *         默认值。
     * @param <T>
     *         数组的元素类型。
     * @return 指定数组的指定下标的元素。
     * @throws IllegalArgumentException
     *         如果参数{@code array}是{@code null}。
     * @throws ArrayIndexOutOfBoundsException
     *         如果参数{@code index}小于{@code 0}。
     */
    public static <T> T getOrDefault(
            T[] array,
            int index,
            T defaultValue
    ) {
        notNull(array, "array");
        if (index < 0) {
            throw new ArrayIndexOutOfBoundsException(index);
        }

        if (index >= array.length) {
            return defaultValue;
        } else {
            return array[index];
        }
    }

    /**
     * 获取指定数组的指定下标的元素，如果超过数组的上限则返回默认值。
     *
     * @param array
     *         数组。
     * @param index
     *         下标。
     * @param defaultValue
     *         默认值。
     * @param <T>
     *         数组的元素类型。
     * @return 指定数组的指定下标的元素。
     * @throws IllegalArgumentException
     *         如果参数{@code array}是{@code null}。
     * @throws ArrayIndexOutOfBoundsException
     *         如果参数{@code index}小于{@code 0}。
     */
    public static <T> T getOrDefault(
            Collection<T> array,
            int index,
            T defaultValue
    ) {
        notNull(array, "array");
        if (index < 0) {
            throw new ArrayIndexOutOfBoundsException(index);
        }

        if (index >= array.size()) {
            return defaultValue;
        } else {
            int i = -1;
            Iterator<T> iterator = array.iterator();
            T result = defaultValue;
            while (i < index) {
                result = iterator.next();
                ++i;
            }
            return result;
        }
    }

    /**
     * 格式化字符串。
     * <p>将占位符{@literal {}}替换为指定的参数。<code>{{</code>被转义为<code>{</code></p>
     *
     * @param f
     *         格式文本。
     * @param values
     *         用于替换的参数。
     * @return 替换的结果，如果参数{@code f}是{@code null}则返回空字符串。
     */
    public static String format(
            String f,
            Object... values
    ) {
        if (f == null) {
            return "";
        }

        // 首先替换所有的占位符，如果给出的参数数量不足则将占位符替换为空字符串。
        return replaceByRegex(STRING_FORMAT_PLACEHOLDER, f, (i, m, last) -> {
            if (values == null) {
                return "";
            } else if (values.length <= i) {
                return "";
            } else {
                final Object value = values[i];
                return value != null ? String.valueOf(value).replace("\\", "\\\\") : "";
            }
        })
                // 然后转义特殊符号。
                .replace("{{", "{")
                .replace("}}", "}");
    }

    /**
     * 扩展路径字符串，将波浪符号扩展为用户目录，将环境变量名扩展为对应的值。
     *
     * @param path
     *         原始路径字符串，自动去掉首尾空格。
     * @return 扩展后的字符串，如果参数{@code path}是{@code null}或者只包含空白字符，则返回空字符串。
     */
    public static String expandPath(
            String path
    ) {
        path = trimToEmpty(path);
        if (path.isEmpty()) {
            return "";
        }

        if (path.startsWith("~")) {
            // 行首的波浪符应当被替换为用户目录。
            path = System.getProperty("user.home", "") + substring(path, 1);
        }

        final String expanded = replaceByRegex(ENV_VARIABLE_NAME, path, (index, matcher, lastMatched) -> {
            final String value = trimToEmpty(System.getenv(matcher.group(1)));
            return value.replace("\\", "\\\\").replace("$", "\\$");
        });

        try {
            // 尝试转化为唯一的绝对路径。
            return new File(expanded).getCanonicalPath();
        } catch (IOException ex) {
            return expanded;
        }
    }

    /**
     * 扩展一个已有的{@link Set}, 将新的枚举元素插入到{@link Set}中。
     *
     * @param <T>
     *         枚举元素的类型。
     * @param clazz
     *         枚举元素的类型。
     * @param src
     *         已有的{@link Set}。
     * @param newElements
     *         需要插入的新枚举元素。
     * @return 包含已有枚举元素和新枚举元素的集合。
     * @throws java.lang.NullPointerException
     *         如果参数{@code clazz}是{@code null}。
     */
    @SafeVarargs
    public static <T extends Enum<T>> Set<T> extendEnumSet(
            Class<T> clazz,
            Set<T> src,
            final T... newElements
    ) {
        notNull(clazz, "clazz");

        final Set<T> ret = src != null && !src.isEmpty() ? EnumSet.copyOf(src) :
                EnumSet.noneOf(clazz);

        if (newElements != null) {
            for (final T newElement : newElements) {
                if (newElement != null) {
                    ret.add(newElement);
                }
            }
        }

        return ret;
    }

    /**
     * 尝试将对象转化为指定的类型。
     *
     * @param <T>
     *         目标类型。
     * @param t
     *         待转化的对象。
     * @param clazz
     *         目标类型。
     * @return 转化的结果，如果参数{@code t}是{@code null}那么返回{@code null}，如果参数{@code t}可以被强制转化为{@code clazz}
     * ，那么返回{@code t}本身，否则也返回{@code null}。
     */
    public static <T> T tryCast(
            Object t,
            Class<T> clazz
    ) {
        notNull(clazz, "clazz");

        if (t != null) {
            if (clazz.isAssignableFrom(t.getClass())) {
                return clazz.cast(t);
            } else {
                return null;
            }
        } else {
            return null;
        }
    }

    /**
     * 将Map中的每个List值通过逗号连接起来并构造新的Map，原Map不受影响。
     * <p>示例：</p>
     * <pre>
     * { "a": [1, 2, 3], "b": [5] } -&gt; { "a": "1,2,3", "b": "5" }
     * </pre>
     *
     * @param src
     *         待处理的原Map。
     * @return 依次将每个值处理后的结果Map。
     */
    public static Map<String, String> flatJoinMap(
            Map<String, List<String>> src
    ) {
        notNull(src, "src");

        final Map<String, String> ret = new HashMap<>(src.size());
        for (final Map.Entry<String, List<String>> entry : src.entrySet()) {
            final String key = trimToEmpty(entry.getKey());
            final Collection<String> values = entry.getValue();
            final String value = String.join(",", values);
            if (!key.isEmpty() && !value.isEmpty()) {
                ret.put(key, value);
            }
        }
        return ret;
    }

    /**
     * 获取指定异常对象的根异常的本地化错误消息。
     *
     * @param t
     *         指定的异常对象。
     * @return 根异常的本地化错误消息，如果参数{@code t == null}，那么返回{@code ""}。
     */
    public static String getRootCauseMessage(
            Throwable t
    ) {
        if (t != null) {
            while (t.getCause() != null)
                t = t.getCause();
            final String ret = t.getLocalizedMessage();
            return ret != null ? ret.trim() : t.toString();
        } else {
            return "";
        }
    }

    /**
     * 按照格式生成异常的信息。
     *
     * @param t
     *         指定的异常。
     * @return 格式化的异常信息，如果参数{@code t}是{@code null}则返回空字符串。
     */
    public static String dumpException(
            Throwable t
    ) {
        if (t == null) {
            return "";
        }

        final StringWriter out = new StringWriter();
        t.printStackTrace(new PrintWriter(out, true));
        return out.toString();
    }

    /**
     * 创建指定的目录，即使此目录已存在也看作创建成功。
     *
     * @param file
     *         需要创建的目录。
     * @throws IllegalArgumentException
     *         如果参数{@code file}是{@code null}，或者{@code file}已存在并且是一个文件。
     * @throws IllegalStateException
     *         如果无法创建指定的目录。
     */
    public static void forceMkDirs(
            File file
    ) {
        notNull(file, "file");

        if (file.isFile()) {
            throw new IllegalArgumentException("file " + file + " already exists");
        }

        if (file.isDirectory()) {
            // 目录已存在，看作创建成功，直接返回。
            return;
        }

        if (!file.mkdirs()) {
            // 如果创建各级目录失败
            if (!file.isDirectory()) {
                // 并且目录仍然不存在，那么创建失败。
                throw new IllegalStateException("cannot make directory " + file);
            }
        }
    }

    /**
     * 创建一个新的临时文件。
     *
     * @return 已创建的临时文件对象。
     * @throws IllegalStateException
     *         如果找不到临时文件目录，或者临时文件目录不可写入，或者临时文件存在且无法删除。
     */
    public static File createTemporaryFile() {
        final String tempDirProperty = trimToEmpty(System.getProperty("java.io.tmpdir"));
        if (tempDirProperty.isEmpty()) {
            throw new IllegalStateException("cannot find temp directory");
        }

        final File tempDir = new File(tempDirProperty);
        if (!tempDir.isDirectory()) {
            throw new IllegalStateException("temp directory \"" + tempDir + "\" is not a directory");
        }

        final String tempFileName = "temp-" + UUID.randomUUID().toString();

        final File result = new File(tempDir, tempFileName);
        if (result.exists()) {
            if (!result.delete()) {
                throw new IllegalStateException("cannot delete exists temp file: " + result);
            }
        }

        return result;
    }

    /**
     * 判断指定的生命周期对象是否可用。
     *
     * @param obj
     *         指定的生命周期对象。
     * @return 如果参数不是{@code obj}不是{@code null}并且可用则返回{@code true}，否则返回{@code false}。
     */
    public static boolean isActive(
            LifeCycle obj
    ) {
        return obj != null && obj.isActive();
    }

    /**
     * 判断指定的掩码是否包含指定的枚举。
     *
     * @param mask
     *         掩码。
     * @param e
     *         枚举值。
     * @param <T>
     *         枚举类型。
     * @return 如果掩码包含了指定的枚举则返回{@code true}，否则返回{@code false}。
     * @throws IllegalArgumentException
     *         如果参数{@code e}是{@code null}。
     */
    public static <T extends Enum<T> & ValueEnum> boolean isEnumSet(
            long mask,
            T e
    ) {
        notNull(e, "e");

        return (e.longValue() & mask) != 0L;
    }

    /**
     * 在掩码中置位指定的枚举，并返回新的掩码。
     *
     * @param mask
     *         原掩码。
     * @param enums
     *         需要置位的枚举。
     * @param <T>
     *         枚举类型。
     * @return 新的掩码。
     */
    @SafeVarargs
    public static <T extends Enum<T> & ValueEnum> long setEnum(
            long mask,
            T... enums
    ) {
        if (enums == null) {
            return mask;
        }

        for (final T e : enums) {
            mask |= e.longValue();
        }

        return mask;
    }

    /**
     * 在掩码中复位指定的枚举，并返回新的掩码。
     *
     * @param mask
     *         原掩码。
     * @param enums
     *         需要复位的枚举。
     * @param <T>
     *         枚举类型。
     * @return 新的掩码。
     */
    @SafeVarargs
    public static <T extends Enum<T> & ValueEnum> long unsetEnum(
            long mask,
            T... enums
    ) {
        if (enums == null) {
            return mask;
        }

        for (final T e : enums) {
            mask &= ~e.longValue();
        }

        return mask;
    }

    /**
     * 用于通过正则表达式进行替换的替换结果生成器。
     */
    @FunctionalInterface
    public interface ReplacementProducer {
        /**
         * 生成替换结果。
         *
         * @param index
         *         序号（从0开始）。
         * @param matcher
         *         当前匹配结果。
         * @param lastMatched
         *         上一次的全匹配结果，如果是第一次匹配则为空字符串。
         * @return 替换结果，如果是{@code null}则看作空字符串。
         */
        String execute(
                int index,
                Matcher matcher,
                String lastMatched
        );
    }

    /**
     * 允许抛出异常的操作。
     *
     * @author Haart
     */
    @FunctionalInterface
    public interface RunnableThrowsException {
        /**
         * 执行操作。
         *
         * @throws Exception
         *         如果操作中出现错误。
         */
        void run()
                throws Exception;
    }

    /**
     * 允许抛出异常的操作, 此操作接受一个指定类型的参数。
     *
     * @param <T>
     *         操作参数的类型。
     * @author Haart
     */
    @FunctionalInterface
    public interface ConsumerThrowsException<T> {
        /**
         * 执行操作。
         *
         * @param t
         *         操作的参数。
         * @throws Exception
         *         如果操作中出现错误。
         */
        void accept(final T t)
                throws Exception;
    }

    /**
     * 允许抛出异常的操作, 此操作返回一个指定类型的结果。
     *
     * @param <T>
     *         指定结果的类型。
     * @author Haart
     */
    @FunctionalInterface
    public interface SupplierThrowsException<T> {
        /**
         * 执行操作。
         *
         * @return 生成的结果。
         * @throws Exception
         *         如果操作中出现错误。
         */
        T get()
                throws Exception;
    }

    /**
     * 允许抛出异常的操作, 此操作接收指定类型的参数, 并返回一个同样类型的结果。
     *
     * @param <T>
     *         参数和指定结果的类型。
     * @author Haart
     */
    @FunctionalInterface
    public interface UnaryOperatorThrowsException<T> {
        /**
         * 执行操作。
         *
         * @param t
         *         操作的参数。
         * @return 生成的结果。
         * @throws Exception
         *         如果操作中出现错误。
         */
        T identity(T t)
                throws Exception;
    }

    /**
     * 允许抛出异常的操作, 此操作接收指定类型的参数, 并返回一个指定类型的结果。
     *
     * @param <T>
     *         参数的类型。
     * @param <R>
     *         生成结果的类型。
     * @author Haart
     */
    @FunctionalInterface
    public interface FunctionThrowsException<T, R> {
        /**
         * 执行操作。
         *
         * @param t
         *         操作的参数。
         * @return 生成的结果。
         * @throws Exception
         *         如果操作中出现错误。
         */
        R apply(T t)
                throws Exception;
    }

    /**
     * 允许抛出异常的操作, 此操作接收指定类型的参数, 并返回布尔值。
     *
     * @param <T>
     *         参数的类型。
     * @author Haart
     */
    @FunctionalInterface
    public interface PredicateThrowsException<T> {
        /**
         * 执行操作。
         *
         * @param t
         *         操作的参数。
         * @return 判断的结果。
         * @throws Exception
         *         如果操作中出现错误。
         */
        boolean test(T t)
                throws Exception;
    }
}
